﻿// Copyright 2006 - Ricardo Stuven (rstuven@gmail.com)
//
// This file is part of MsSqlSpatial.
// MsSqlSpatial is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// MsSqlSpatial is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public License
// along with MsSqlSpatial; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using MsSqlSpatial;
using GisSharpBlog.NetTopologySuite.Geometries;
using GeoAPI.Geometries;

public partial class TableValuedFunctions
{
	class SpatialQuery
	{
		internal struct Arguments
		{
			public SqlDouble double1;
			public SqlString string1;

			public Arguments(SqlDouble _double1)
			{
				double1 = _double1;
				string1 = SqlString.Null;
			}

			public Arguments(SqlString _string1)
			{
				string1 = _string1;
				double1 = SqlDouble.Null;
			}

			public static Arguments Empty
			{
				get
				{
					Arguments args;
					args.double1 = SqlDouble.Null;
					args.string1 = SqlString.Null;
					return args;
				}
			}
//
//			public bool IsEmpty
//			{
//			    get { return double1.IsNull && string1.IsNull; }
//			}
		}

		internal delegate IEnumerable ReaderHandler(SqlDataReader reader, IGeometry geometry, Arguments args);

		internal static IEnumerable Execute(
			SqlString table_name,
			SqlString column_name,
			SqlBytes ewkbMBR,
			SpatialQuery.ReaderHandler handler)
		{
			return Execute(table_name, column_name, ewkbMBR, null, SqlString.Null, Arguments.Empty, handler);
		}

		internal static IEnumerable Execute(
			SqlString table_name,
			SqlString column_name,
			SqlBytes ewkbMBR,
			SqlString condition,
			SpatialQuery.ReaderHandler handler)
		{
			return Execute(table_name, column_name, ewkbMBR, null, condition, Arguments.Empty, handler);
		}

		internal static IEnumerable Execute(
			SqlString table_name,
			SqlString column_name,
			SqlBytes ewkbMBR,
			SpatialQuery.Arguments args,
			SpatialQuery.ReaderHandler handler)
		{
			return Execute(table_name, column_name, ewkbMBR, null, SqlString.Null, args, handler);
		}

		internal static IEnumerable Execute(
			SqlString table_name,
			SqlString column_name,
			SqlBytes ewkbMBR,
			SqlString condition,
			SpatialQuery.Arguments args,
			SpatialQuery.ReaderHandler handler)
		{
			return Execute(table_name, column_name, ewkbMBR, null, condition, args, handler);
		}


		internal static IEnumerable Execute(
			SqlString table_name,
			SqlString column_name,
			SqlBytes ewkbMBR,
			SqlBytes ewkb,
			SpatialQuery.Arguments args,
			SpatialQuery.ReaderHandler handler)
		{
			return Execute(table_name, column_name, ewkbMBR, ewkb, SqlString.Null, args, handler);
		}

		internal static IEnumerable Execute(
			SqlString table_name,
			SqlString column_name,
			SqlBytes ewkbMBR,
			SqlBytes ewkb,
			SqlString condition,
			SpatialQuery.Arguments args,
			SpatialQuery.ReaderHandler handler)
		{
			if (ewkbMBR == null || ewkbMBR.IsNull)
				ewkbMBR = ewkb;

			IGeometry geometryMBR = ConvertGeometry.ToGeometry(ewkbMBR);

			IGeometry geometry;
			if (ewkb == null || ewkb.IsNull)
				geometry = geometryMBR;
			else
				geometry = ConvertGeometry.ToGeometry(ewkb);

			using (SqlConnection connection = DatabaseHelper.ContextConnection)
			{
				connection.Open();
				using (SqlCommand command = connection.CreateCommand())
				{
					string tableName = table_name.Value;
					string columnName = column_name.Value;
	
					string schemaName = "";
					DatabaseHelper.SplitSchemaAndTableName(command, ref tableName, ref schemaName);

					string primaryKey = GeometryTable.GetPrimaryKeyColumn(command, schemaName, tableName);
	
					string filterQuery = "";
	
					if (geometryMBR != null && !geometryMBR.IsEmpty)
					{
						if (!condition.IsNull && condition.Value.Trim().Length != 0)
							filterQuery = " AND (" + condition.Value + ")";
						IEnvelope mbr = geometryMBR.EnvelopeInternal;
						DatabaseHelper.BuildCommand(command, string.Format(
@"SELECT [{4}], [{2}]
FROM [{0}].[{1}] WITH (INDEX(IDX_{2}_Envelope))
WHERE 
(   [{2}_Envelope_MinX] < @p0
AND [{2}_Envelope_MaxX] > @p1
AND [{2}_Envelope_MinY] < @p2
AND [{2}_Envelope_MaxY] > @p3
){3}"
, schemaName		// {0}
, tableName			// {1}
, columnName		// {2}
, filterQuery		// {3}
, primaryKey		// {4}
)
, mbr.MaxX
, mbr.MinX
, mbr.MaxY
, mbr.MinY
);
					}
					else
					{
						if (!condition.IsNull && condition.Value.Trim().Length != 0)
							filterQuery = "WHERE " + condition.Value;
						DatabaseHelper.BuildCommand(command, string.Format(
@"SELECT [{4}], [{2}]
FROM [{0}].[{1}]
{3}"
, schemaName		// {0}
, tableName			// {1}
, columnName		// {2}
, filterQuery		// {3}
, primaryKey		// {4}
));
					}
	
					using (SqlDataReader reader = command.ExecuteReader())
					{
						return handler.Invoke(reader, geometry, args);
					}
				}
			}
		}

		internal const string TableDefinition = "oid bigint";
		internal const string FillRow = "FillRowSpatialQuery";

	}

	public static void FillRowSpatialQuery(Object obj, out SqlInt64 oid)
	{
		oid = new SqlInt64((Int64)obj);
	}

}
